package com.pb.wkflow.core.service.impl;

import com.pb.wkflow.api.controller.request.ProcessTaskRequest;
import com.pb.wkflow.core.entity.ActHiTaskEntity;
import com.pb.wkflow.core.mapper.ActHiTaskMapper;
import com.pb.wkflow.core.service.WorkflowTaskService;
import com.pb.wkflow.core.utils.AssertUtils;
import com.pb.wkflow.core.utils.StringUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.impl.RepositoryServiceImpl;
import org.camunda.bpm.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.pvm.PvmActivity;
import org.camunda.bpm.engine.impl.pvm.PvmTransition;
import org.camunda.bpm.engine.impl.pvm.ReadOnlyProcessDefinition;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.impl.pvm.process.TransitionImpl;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 苗益辉
 * @date 2022/6/20 21:34
 */
@Service
public class WorkflowTaskServiceImpl implements WorkflowTaskService {

    @Autowired
    TaskService taskService;

    @Autowired
    HistoryService historyService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    ActHiTaskMapper actHiTaskMapper;

    @Override
    public void setTaskComment(String taskId, String comment, String processInstanceId) {
        taskService.createComment(taskId, processInstanceId, comment);
    }

    @Override
    public List<Comment> findTaskComments(String taskId) {
        return taskService.getTaskComments(taskId);
    }

    @Override
    public List<Task> findTaskList(String processInstanceId) {
        return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    }

    @Override
    public Task findTask(String taskId) {
        return taskService.createTaskQuery().taskId(taskId).singleResult();
    }

    @Override
    public List<Task> findTaskByProcDefineId(String procDefineId) {
        return taskService.createTaskQuery().processInstanceId(procDefineId).list();
    }

    @Override
    public void setAssignee(String taskId, String userId) {
        taskService.setAssignee(taskId, userId);
    }

    @Override
    public void deleteProcessInstance(String processInstanceId, String deleteReason) {
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
    }

    @Override
    public void setTaskVariableLocal(String taskId, Map<String, Object> variableLocal) {
        taskService.setVariablesLocal(taskId, variableLocal);
    }

    @Override
    public void setTaskVariables(String taskId, Map<String, Object> variables) {
        taskService.setVariables(taskId, variables);
    }

    @Override
    public void claimTask(String taskId, String userId) {
        taskService.claim(taskId, userId);
    }

    @Override
    public void completeTask(String taskId) {
        taskService.complete(taskId);
    }

    @Override
    public ProcessInstance findProcess(String processInstanceId) {
        return runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    }

    @Override
    @Transactional(readOnly = false)
    public void passProcess(String taskId, Map<String, Object> variables) {
        List<Task> taskList = taskService.createTaskQuery().taskId(taskId)
                .taskDescription("jointProcess").list();
        for (Task task: taskList) {
            commitProcess(task.getId(), null, null);
        }
        commitProcess(taskId, variables, null);
    }

    @Override
    public void commitProcess(String taskId, Map<String, Object> variables, String activityId) {
        if (variables == null) {
            variables = new HashMap<>(1);
        }
        //跳转节点为空，默认提交操作
        if (StringUtils.isBlank(activityId)) {
            taskService.complete(taskId,variables);
        } else {
            turnTransition(taskId, variables, activityId);
        }
    }

    @Override
    public void checkAndClaimTask(String userId, String taskId) {
        Task task = findTask(taskId);
        AssertUtils.notNull(task, "该任务已被处理");
        String assignee = task.getAssignee();
        if (StringUtils.isNotBlank(assignee)) {
            //任务已被领取
            AssertUtils.notTrue(assignee.equals(userId), "该任务已被他人领取");
        }
    }

    @Override
    public void submitTask(ProcessTaskRequest request) {
        if (StringUtils.isNotBlank(request.getComment())) {
            taskService.createComment(request.getTaskId(), request.getProcessInstanceId(), request.getComment());
        }
        String activityId = null;
        if (StringUtils.isNotBlank(request.getActivityId())) {
            activityId = request.getActivityId();
        }
        commitProcess(request.getTaskId(), request.getGlobalVariables(), activityId);
    }

    /**
     * 根据任务id查询已完成的任务
     * @param taskId 任务id
     * @return HistoricTaskInstance
     */
    @Override
    public HistoricTaskInstance findHistoricTask(String taskId) {
        return historyService.createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .singleResult();
    }

    /**
     * 查询任务本地变量
     * @param taskId 任务id
     * @param key 键值
     * @return Object
     */
    @Override
    public Object getVariableLocal(String taskId, String key) {
        return taskService.getVariableLocal(taskId, key);
    }

    /**
     * 设置任务本地变量
     * @param taskId 任务id
     * @param key 键
     * @param value 值
     */
    @Override
    public void setVariableLocalByKey(String taskId, String key, Object value) {
        taskService.setVariableLocal(taskId, key, value);
    }

    /**
     * 删除任务本地变量
     * @param taskId 任务id
     * @param key 键
     */
    @Override
    public void removeVariableLocal(String taskId, String key) {
        taskService.removeVariableLocal(taskId, key);
    }

    /**
     * 流程转向操作
     * @param taskId 当前任务id
     * @param variables 流程变量
     * @param activityId 目标节点任务id
     */
    public void turnTransition (String taskId, Map<String, Object> variables, String activityId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //查询当前任务节点的节点信息
        ActivityImpl currentActivity = queryCurrentTask(task.getId(), task.getTaskDefinitionKey());
        //查询需要跳转任务的节点信息
        ActivityImpl targetActivity = queryTargetActivity(task.getProcessDefinitionId(), activityId);
        AssertUtils.notNull(targetActivity, "目标节点不存在");
        //通过活动可以获得流程将要出去的路线
        List<PvmTransition> outgoingTransition = currentActivity.getOutgoingTransitions();
        List<PvmTransition> originPvmTransitionList = new ArrayList<>();
        boolean isExclusiveGatewayActivityId = false;
        //判断要跳转的节点是否在排他网关后面
        if (outgoingTransition.size() == 1 && outgoingTransition.get(0).getDestination().getActivityBehavior() instanceof ExclusiveGatewayActivityBehavior) {
            PvmActivity exclusiveGateway = outgoingTransition.get(0).getDestination();
            List<PvmTransition> exclusiveGatewayOutgoingTransitions = exclusiveGateway.getOutgoingTransitions();
            //判断要提交的节点是否在排他网关后面
            for (PvmTransition exclusiveGatewayOutgoingTransition : exclusiveGatewayOutgoingTransitions) {
                String id = exclusiveGatewayOutgoingTransition.getDestination().getId();
                if (activityId.equals(id)) {
                    isExclusiveGatewayActivityId = true;
                    break;
                }
            }
        }
        if (isExclusiveGatewayActivityId) {
            PvmActivity exclusiveActivity = outgoingTransition.get(0).getDestination();
            List<PvmTransition> exclusiveGatewayOutgoingTransitions = exclusiveActivity.getOutgoingTransitions();
            for (PvmTransition exclusiveGatewayOutgoingTransition : exclusiveGatewayOutgoingTransitions) {
                if (!activityId.equals(exclusiveGatewayOutgoingTransition.getDestination().getId())) {
                    originPvmTransitionList.add(exclusiveGatewayOutgoingTransition);
                }
            }
            //去除目标节点之外的其他节点
            exclusiveGatewayOutgoingTransitions.removeAll(originPvmTransitionList);
            String defaultId = (String) exclusiveActivity.getProperty("default");
            ((ActivityImpl) exclusiveActivity).setProperty("default", exclusiveGatewayOutgoingTransitions.get(0).getId());
            taskService.complete(task.getId(), variables);

            //提交后恢复
            exclusiveGatewayOutgoingTransitions.addAll(originPvmTransitionList);
            ((ActivityImpl) exclusiveActivity).setProperty("default", defaultId);
        } else {
            for (PvmTransition pvmTransition : outgoingTransition) {
                originPvmTransitionList.add(pvmTransition);
            }
            outgoingTransition.clear();
            TransitionImpl transitionImpl = currentActivity.createOutgoingTransition(activityId);
            transitionImpl.setDestination(targetActivity);
            taskService.complete(task.getId(), variables);
            currentActivity.getOutgoingTransitions().remove(transitionImpl);
            for (PvmTransition pvmTransition : originPvmTransitionList) {
                outgoingTransition.add(pvmTransition);
            }
        }
        //重新刷新流程定义缓存
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutorTxRequired();
        commandExecutor.execute(commandContext -> {
            commandContext.getProcessEngineConfiguration().getDeploymentCache().removeProcessDefinition(currentActivity.getProcessDefinition().getId());
            return null;
        });
    }

    public ActivityImpl queryCurrentTask(String processInstanceId, String taskDefineKey) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId)
                .list();
        return queryTargetActivity(taskList.get(0).getProcessDefinitionId(), taskDefineKey);
    }

    public ActivityImpl queryTargetActivity(String processDefineId, String taskDefineKey) {
        ReadOnlyProcessDefinition definition = ((RepositoryServiceImpl) repositoryService).
                getDeployedProcessDefinition(processDefineId);
        List<ActivityImpl> activities = (List<ActivityImpl>) definition.getActivities();
        for (ActivityImpl activity : activities) {
            if (activity.getId().equals(taskDefineKey) || activity.getId().contains(taskDefineKey)) {
                return activity;
            }
        }
        return null;
    }
}
